package com.rengwuxian.coursecoroutines._2_structured_concurrency

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.concurrent.thread
import kotlin.coroutines.EmptyCoroutineContext

/**
 * 协程的结构化异常管理
 * 协程的异常、协程的取消、try/catch
 *
 * 协程的异常管理：很难理解怎么用
 *
 * 协程的try/catch：
 * scope.launch {
 *    try {
 *        throw IllegalStateException("Wrong user!")
 *    } catch (e: IllegalStateException) {
 *    }
 * }
 * 只能在协程内部捕获异常，如果将try/catch挪到launch的外面，则无法捕获协程内部的异常，因为try/catch无法穿透到协程内部，只会包含启动协程的部分。
 *
 * 协程的异常取消双向传播验证，如下方代码，输出的log结果：
 * Parent started
 * Grand child started
 * isActive: parent - true, child - true
 * isCancelled: parent - false, child - false
 * Exception in thread "DefaultDispatcher-worker-3" java.lang.IllegalStateException: User invalid!
 * 	at com.rengwuxian.coursecoroutines._2_structured_concurrency._8_exceptionKt$main$1$parentJob$1$1.invokeSuspend(8_exception.kt:59)
 * 	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
 * 	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:102)
 * 	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:589)
 * 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:816)
 * 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:720)
 * 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:707)
 * 	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@680c27bc, Dispatchers.Default]
 * isActive: parent - false, child - false
 * isCancelled: parent - true, child - true
 *
 * 附：
 * 抛异常就崩溃不是JVM的机制，而是Android的规则，是Android额外加的逻辑，
 * 在普通的JVM应用里，抛异常只会导致线程崩溃，而不会导致整个应用崩溃。
 *
 * 第一个区别：
 * 1.取消流程：抛CancellationException 和 Job.cancel，只取消当前协程和对应的子协程（单向传播）
 * 2.异常流程：除CancellationException以外的异常，会取消当前协程和对应的子协程、父协程；二者还会继续传播，只到所有协程都取消。（双向传播）
 *   整个往上往下整个父子协程树都会因为这个异常而被取消掉。
 * 二者设计思想的控制流程一样（同一套流程机制），只不过协程的取消流程CancellationException是简化版本的。而异常流程是全量版本
 * 源码：
 * JobSupport 中：
 *     public open fun childCancelled(cause: Throwable): Boolean {
 *         if (cause is CancellationException) return true
 *         return cancelImpl(cause) && handlesException
 *     }
 * 每个协程被取消时，它的父协程都会被调用这个函数，如果是非CancellationException异常，就会调用cancelImpl来取消它自己。
 * 普通取消，不会取消父协程，异常取消，就会取消父协程。
 * 为什么这么设计：如果子协程异常，父协程也失去了意义。整个流程都完成了，才算整个任务都完成了，如果有一个坏掉，则意味着整个协程树的损坏，故而都取消。
 * 而如果想要子协程抛异常，父协程不想被取消，则可以使用SupervisorJob(),它取消，整个协程都会取消，而内部的异常，不会影响到父协程。
 *
 * 第二个区别：
 * 异常流程只有抛异常这一个途径来触发。
 * 取消流程可以协程内部抛CancellationException（以及子类）和job.cancel()来触发。
 *
 * 第三个区别：
 * CancellationException的作用只是取消协程
 * 而异常，不仅会取消协程，也给把异常暴露给线程世界。（除非注册了CoroutineExceptionHandler，可以阻止异常外抛到线程世界）
 */
fun main() = runBlocking<Unit> {
  val scope = CoroutineScope(EmptyCoroutineContext)
  var childJob: Job? = null
  val parentJob = scope.launch {
    childJob = launch {
      launch {
        println("Grand child started")
        delay(3000)
        println("Grand child done")
      }
      delay(1000)
      throw IllegalStateException("User invalid!")
    }
    println("Parent started")
    delay(3000)
    println("Parent done")
  }
  delay(500)
  CoroutineExceptionHandler
//  parentJob.cancel()
  println("isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive}")
  println("isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled}")
  delay(1000)
  println("isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive}")
  println("isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled}")
  delay(10000)
}